package com.sinosoft.brokerchannel.base.server;


import com.sinosoft.brokerchannel.base.bins.BasChannelSet;

import com.sinosoft.brokerchannel.base.bins.TGatewayChannelInfo;

import com.sinosoft.brokerchannel.base.common.SystemParamFinal;
import com.sinosoft.brokerchannel.base.model.AbstractRequest;
import com.sinosoft.brokerchannel.base.model.AbstractResponse;
import com.sinosoft.brokerchannel.base.model.RequestHead;
import com.sinosoft.brokerchannel.base.model.ResponseHead;
import com.sinosoft.brokerchannel.base.utile.BusinessException;
import com.sinosoft.brokerchannel.base.utile.ExceptionUtil;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 需求名称:非车理赔渠道平台<br/>
 * 类描述:[逻辑处理公共处理类]<br/>
 * @author [shilk]
 * @date [2017年9月22日 上午9:38:51]
 * @version [v1.0]
 */
@Data
public abstract class AbstractService {
	protected Logger logger = LoggerFactory.getLogger(this.getClass());
	@Autowired
	private LogService logService;
	@Autowired
	private ChannelInfoService channelInfoService;
	/**
	 * 向核心请求
	 */
	public final static String REQ_TO_CLAIM = "claim";

	/**
	 * 请求类型（作为接口名称）
	 */
	private String requestType;

	/**
	 * 赔案号/报案号
	 */
	private String claimNo;

	/**
	 * 当前时间
	 */
	private String currentTm;

	/**
	 * 渠道代码
	 */
	private String channelNo;

	/**
	 * 渠道平台是否服务方 1服务方 0调用方
	 */
	private String serviceFlag;

	/**
	 * 被请求时报文日志ID
	 */
	private String reqLogId;

	/**
	 * 请求时报文日志ID
	 */
	private String reqOthLogId;

	/**
	 * 报文格式
	 */
	private String dataType;

	/**
	 * 异常信息
	 */
	private String exceptionMess;

	/**
	 * UUID，请求响应使用同一个UUID
	 */
	private String uuid;

	/**
	 * 接口名称
	 */
	private String serviceName;

	/**
	 * 时间戳，防止消息重放
	 */
	private Long timestamp;

	/**
	 * 接口编码
	 */
	private String interfaceCode;

	/**
	 * 渠道信息
	 */
	private BasChannelSet basChannelSet;

	/**
	 * 合作伙伴编码
	 */
	private String companyCode;

	/**
	 * 加密参数
	 */
	private String seed;

	/**
	 * 对接系统的名称
	 */
	private String otherSysName;

	/**
	 * 接口发送时间
	 */
	private String sendTm;
	//add gzj
	/**
	 * 请求头信息
	 */
	private RequestHead requestHead;
	/**
	 * 响应头信息
	 */
	private ResponseHead responseHead;
	/**
	 * 交易名称
	 */
	private String transactionName;

	/**
	 * @param transactionName
	 * @param requestHead     {@link RequestHead}
	 * @throws BusinessException
	 * @描述:初始化参数，赋值
	 */
	public void init(String transactionName, RequestHead requestHead) {
		this.transactionName = transactionName;
		this.requestHead = requestHead;
		this.responseHead = new ResponseHead();
	}

	/**
	 * @param request
	 * @return 由uuid生成的流水号
	 * @描述:交易前记录日志
	 */
	public String beforeTheTransaction(AbstractRequest request) throws BusinessException {
		logger.info("----------------------------------------" + transactionName + "开始----------------------------------------");
		String flowid = UUID.randomUUID().toString();
		logService.objectPrint("请求数据（" + flowid + ")", request);
		if (request.getRequestBody() == null) {
			throw new BusinessException(SystemParamFinal.undefinedExceptionCode, "请求主信息不能为空");
		}
		if (request.getRequestBody().getChannel() == null) {
			throw new BusinessException(SystemParamFinal.undefinedExceptionCode, "渠道信息不能为空");
		}
//		TGatewayChannelInfo channelInfo =
		TGatewayChannelInfo channelInfo = channelInfoService.getChannelInfo(request.getRequestBody().getChannel().getChannelCode());
		System.out.println(channelInfo);
		if (channelInfo == null) {
			throw new BusinessException(SystemParamFinal.undefinedExceptionCode, "没有获取到渠道信息，检查渠道代码是否正确");
		}
		if (!channelInfo.getChannelServices().contains(request.getRequestBody().getChannel().getChannelTradeCode())) {
			throw new BusinessException(SystemParamFinal.undefinedExceptionCode, "没有权限访问（" + transactionName + "）服务");
		}
		//把渠道信息放入HttpServletRequest对象中
		HttpServletRequest servletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		if (servletRequest != null) {
			servletRequest.setAttribute("channelCode", channelInfo.getChannelCode());
		}
		return flowid;
	}

	/**
	 * @param response
	 * @param flowid
	 * @描述:交易后更新日志
	 */
	public void afterTheTransaction(AbstractResponse response, String flowid) {
		logService.objectPrint("返回数据（" + flowid + ")", response);
		logger.info("----------------------------------------" + transactionName + "结束----------------------------------------");
	}
	/**
	 * 异常处理
	 */
	public void exceptionAction(Exception e) {
		responseHead.setResponseCode(com.sinosoft.brokerchannel.base.common.SystemParamFinal.EXCEPTION_RESPONSE_CODE);
		responseHead.setErrorCode(com.sinosoft.brokerchannel.base.common.SystemParamFinal.EXCEPTION_ERROR_CODE);
		responseHead.setErrorMessage(ExceptionUtil.getMessage(com.sinosoft.brokerchannel.base.common.SystemParamFinal.SYSTEM_UNKONWN_EXCEPTION) + e.getClass().getName());
		responseHead.setEsbCode(com.sinosoft.brokerchannel.base.common.SystemParamFinal.EXCEPTION_ERROR_CODE);
		responseHead.setEsbMessage(ExceptionUtil.getMessage(com.sinosoft.brokerchannel.base.common.SystemParamFinal.SYSTEM_UNKONWN_EXCEPTION) + e.getClass().getName());
	}

	/**
	 * 业务异常处理
	 */
	public void businessExceptionAction(BusinessException e) {
		responseHead.setResponseCode(SystemParamFinal.EXCEPTION_RESPONSE_CODE);
		responseHead.setErrorCode(e.getExceptionCode());
		responseHead.setErrorMessage(e.getExceptionMessage());
		responseHead.setEsbCode(e.getExceptionCode());
		responseHead.setEsbMessage(e.getExceptionMessage());
	}

	/**
	 * 成功处理
	 */
	public void sucessAction() {
		responseHead.setResponseCode(com.sinosoft.brokerchannel.base.common.SystemParamFinal.SUCCESS_RESPONSE_CODE);
		responseHead.setErrorCode(com.sinosoft.brokerchannel.base.common.SystemParamFinal.SUCCESS_ERRO_CODE);
		responseHead.setErrorMessage(ExceptionUtil.getMessage(com.sinosoft.brokerchannel.base.common.SystemParamFinal.SUCCESS));
		responseHead.setEsbCode(com.sinosoft.brokerchannel.base.common.SystemParamFinal.SUCCESS_ESB_CODE);
		responseHead.setEsbMessage(ExceptionUtil.getMessage(com.sinosoft.brokerchannel.base.common.SystemParamFinal.SUCCESS));
	}

	/**
	 * 结果为失败，在响应头返回错误信息（目前主要用于b2b调用业绩查询和保单列表查询接口）
	 * 添加时间：2018-02-08
	 * @param errMessage 错误信息
	 */
	public void errorAction(String errMessage){
		responseHead.setResponseCode(com.sinosoft.brokerchannel.base.common.SystemParamFinal.EXCEPTION_RESPONSE_CODE);
		responseHead.setErrorCode(com.sinosoft.brokerchannel.base.common.SystemParamFinal.EXCEPTION_ERROR_CODE);
		responseHead.setErrorMessage(errMessage);
		responseHead.setEsbCode(com.sinosoft.brokerchannel.base.common.SystemParamFinal.EXCEPTION_ERROR_CODE);
		responseHead.setEsbMessage(errMessage);
	}
	/**
	 * 收集接口日志信息，用于日志易提取信息
	 *
	 * @param serviceName 服务名称String
	 * @param request     服务请求对象Object
	 * @param response    服务返回对象Object
	 * @param useTime     用时 long
	 * @return map
	 */
	protected Map<String, Object> interfaceLogCollect(String serviceName, Object request, Object response, long useTime) {
		Map<String, Object> map = new HashMap<>();
		try {
			map.put("serviceName", serviceName); //服务名称
			map.put("request", request); //请求对象
			map.put("response", response); //返回对象
			map.put("use_time", useTime); //耗时
			logger.info("zonghaoshi-----------" + useTime);
		} catch (Exception e) {
			logger.info("日志收集异常：", e);
			//日志收集异常不予处理，不能影响正常流程
		}
		return map;
	}

	/**
	 * 创建新的ServletRequest对象，替换输入流
	 *
	 * @param request
	 * @param requestBody
	 * @param charset
	 * @return
	 * @throws Exception
	 */
	public ServletRequest createServletRequest(HttpServletRequest request, String requestBody, String charset) throws UnsupportedEncodingException {
		return new HttpServletRequestWrapper(request) {
			@Override
			public String getContentType() {
				if (request.getContentType().contains("text/xml")) {
					return request.getContentType().replace("text/xml", "application/xml");
				}
				return request.getContentType();
			}

			@Override
			public ServletInputStream getInputStream() throws UnsupportedEncodingException {
				return new ServletInputStream() {
					private byte[] buf;   //请求数据
					private int pos;      //当前位置
					private int count;    //数据长度

					@Override
					public synchronized int read() {
						return (pos < count) ? (buf[pos++] & 0xff) : -1;
					}

					@Override
					public boolean isFinished() {
						return false;
					}

					@Override
					public boolean isReady() {
						return false;
					}

					@Override
					public void setReadListener(ReadListener listener) {
					}

					/**
					 * @param input
					 * @return
					 */
					public ServletInputStream create(String input) throws UnsupportedEncodingException {
						try {
							this.buf = input.getBytes(charset);
							this.pos = 0;
							this.count = buf.length;
						} catch (Exception e) {
							logger.info(com.sinosoft.brokerchannel.base.common.SystemParamFinal.Exception_MSG, e);
							throw e;
						}
						return this;
					}
				}.create(requestBody);
			}
		};
	}

	@ApiOperation(value = "异常处理", hidden = true)
	@ExceptionHandler(Exception.class)
	@ResponseBody
	public String exceptionHandler(Exception e) {
		logger.info(com.sinosoft.brokerchannel.base.common.SystemParamFinal.Exception_LOG, e);
		return com.sinosoft.brokerchannel.base.common.SystemParamFinal.Exception_MSG;
	}
}
